package org.nlp2rdf.navigator.component.store;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.ValidationException;
import org.exolab.castor.xml.XMLContext;
import org.nlp2rdf.navigator.client.common.LearnedConceptDTO;
import org.nlp2rdf.navigator.client.common.StoredConceptDTO;
import org.nlp2rdf.navigator.component.exception.ComponentInitException;
import org.nlp2rdf.navigator.component.exception.ConceptSavingFailedException;
import org.springframework.core.io.Resource;

/**
 * 
 * Stores the objects as serialized java object to a file, as given in filename
 * 
 * @author Sebastian Hellmann <hellmann@informatik.uni-leipzig.de>
 * 
 */
public class XMLFileStore extends Store implements IStore {
	private static final Logger logger = Logger
			.getLogger(SimpleFileStore.class);
	private Map<String, StoredConceptDTO> conceptMap = null;
	private String filename;
	
	private Resource mapping;


	private Marshaller marshaller;
	private Unmarshaller unmarshaller;
	
	public void setMapping(Resource mapping) {
		this.mapping = mapping;
	}
	
	
	public Map<String, StoredConceptDTO> getConceptMap() {
		return conceptMap;
	}
	
	public void setConceptMap(Map<String, StoredConceptDTO> conceptMap) {
		this.conceptMap = conceptMap;
	}



	public void setFilename(String filename) {
		this.filename = filename+".xml";
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nlp2rdf.navigator.server.store.Store#_init()
	 */
	@SuppressWarnings("unchecked")
	@Override
	protected void _init() throws ComponentInitException {
		
		//initialize the serializer
		setUpMarshaller();
		
		this.filename = getComponentConfig().getPathModifier() + this.filename;
		File f = new File(filename);
		conceptMap = new HashMap<String, StoredConceptDTO>();
		if (f.exists()) {
			try {
				conceptMap = ((XMLFileStore)unmarshaller.unmarshal(new FileReader(f))).getConceptMap();
                updateRelatedConceptStrategies(conceptMap);
			} catch (Exception e) {
				logger.error("unable to unmashal concepts, because of:", e);
			} 
//			conceptMap = (Map<String, StoredConceptDTO>) Files
//					.readObjectfromFile(f);
			logger.debug("Loaded " + conceptMap.size() + " concepts from "
					+ filename);
		} else {
			logger.warn("No saved concepts found in " + filename);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.nlp2rdf.navigator.server.store.IStore#loadConcepts()
	 */
	public List<StoredConceptDTO> getStoredConcepts() {
		check();
		return new ArrayList<StoredConceptDTO>(conceptMap.values());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.nlp2rdf.navigator.server.store.Store#_saveConcept(org.nlp2rdf.navigator
	 * .client.common.LearningResultDTO)
	 */
	@Override
	protected StoredConceptDTO _saveConcept(LearnedConceptDTO learningResultDTO)
			throws ConceptSavingFailedException {
		check();
		StoredConceptDTO s = new StoredConceptDTO(learningResultDTO,
				generateUriFromLabel(learningResultDTO));
		put(s);
		return s;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.nlp2rdf.navigator.server.store.IStore#incrementHitCount(org.nlp2rdf
	 * .navigator.client.common.LearningResultDTO)
	 */
	public void incrementHitCount(StoredConceptDTO learningResultDTO) {
		check();
		if (conceptMap.get(learningResultDTO.getManchesterSyntaxString()) != null) {
			conceptMap.get(learningResultDTO.getManchesterSyntaxString())
					.incrementHitcounter();
		}
		_saveConcept(learningResultDTO);
	}

	private void put(StoredConceptDTO learningResultDTO) {
		String key = learningResultDTO.getManchesterSyntaxString();
		conceptMap.put(key, learningResultDTO);
		
		synchronized (conceptMap) {
			File f = new File(filename);
			
			try {
				marshaller.setWriter(new FileWriter(f));
				marshaller.marshal(this);
				logger.debug("Saved " + conceptMap.size() + " concepts to " + filename);
			} catch (Exception e) {
				logger.error("Uanble to marshal, because of",e);
			} 
			//Files.writeObjectToFile(conceptMap, f);
			
		}
		
	}
	
	private void setUpMarshaller() throws ComponentInitException{
		
		try {
			XMLContext context = new XMLContext();
			Mapping mapping = new Mapping();
			mapping.loadMapping(this.mapping.getURL());
			context.addMapping(mapping);
			marshaller = context.createMarshaller();
			unmarshaller = context.createUnmarshaller();
			//unmarshaller.setClass(this.getClass());
		} catch (Exception e) {
			logger.error("unable to create marshallers", e);
            throw new ComponentInitException("unable to create marshallers", e);
		}

	}

}
